﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
using System.Diagnostics;
using System.ComponentModel;

namespace VLCMultyRemote
{
    /// <summary>
    /// represents a connection to a TCP/IP peer
    /// can recive and send messages
    /// </summary>
    public class Connection
    {
        /// <summary>
        /// port for the connection
        /// </summary>
        private int port;

        /// <summary>
        /// ip adress for the connection
        /// </summary>
        private IPAddress ipAddress;

        /// <summary>
        /// communication socket
        /// </summary>
        private Socket socket;

        /// <summary>
        /// thread for reading
        /// </summary>
        private Thread threadForCommandListener;


        public delegate void MessageRecievingEventHandler(object sender, String message);
        
        /// <summary>
        /// event for statusmessages of the connection
        /// </summary>
        public event MessageRecievingEventHandler OnStatusChanged;
        
        /// <summary>
        /// event for incomming messages
        /// </summary>
        public event MessageRecievingEventHandler OnDataRecievingControl;

        /// <summary>
        /// is true if the connection needs to be closed
        /// </summary>
        private bool connectionFinishing = false;

        private Client connectionManager;

        /// <summary>
        /// prepare a connection to a given IPEndPoint
        /// </summary>
        /// <param name="ipEndPoint"></param>
        public Connection(IPEndPoint ipEndPoint, Client connectionManager)
        {
            this.connectionManager = connectionManager;
            port = ipEndPoint.Port;
            ipAddress = ipEndPoint.Address;
        }

        /// <summary>
        /// starts the communication in a own thread
        /// </summary>
        public void startCommunication()
        {
            threadForCommandListener = new Thread(new ThreadStart(listenForCommandResponse));
            threadForCommandListener.Start();
        }

        /// <summary>
        /// stop the communication
        /// </summary>
        public void finishCommunication()
        {
            connectionFinishing = true;

            try
            {
                sendCommand("logout");
                threadForCommandListener.Abort();
                socket.Close();
            }
            catch (Exception)
            { 
            }
            
            if (OnStatusChanged != null)
            {
                OnStatusChanged(this, ipAddress.ToString() + ":" + port + " Connection closed\n");
            }
        }

        /// <summary>
        /// waits for incomming messages for the connection
        /// need to run in a own thread
        /// </summary>
        private void listenForCommandResponse()
        {
            socket = null;
            IPHostEntry hostEntry = Dns.GetHostEntry(ipAddress);
            
            do
            {
                IPEndPoint ipe = new IPEndPoint(ipAddress, port);
                Socket tempSocket =
                    new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                try
                {
                    tempSocket.Connect(ipe);
                }
                catch
                {
                }

                if (tempSocket.Connected)
                {
                    socket = tempSocket;
                }

                if (socket == null)
                {
                    try
                    {
                        if (OnStatusChanged != null)
                        {
                            OnStatusChanged(this, "Error opening socket\n");
                        }
                    }
                    catch (Exception e)
                    {
                        throw e;
                    }
                    Thread.Sleep(1000);
                }
                else
                {
                    NetworkStream ns = new NetworkStream(socket);
                    StreamReader sr = new StreamReader(ns, Encoding.UTF8);
                    try
                    {
                        connectionManager.Connected = true;

                        if (OnStatusChanged != null)
                        {
                            OnStatusChanged(this, ipAddress.ToString() + ":" + port + " Connected\n");
                        }
                    }
                    catch (Exception e)
                    {
                        throw e;
                    }
                    while (socket != null)
                    {
                        try
                        {
                            String line = sr.ReadLine();

                            if (line == null) 
                                throw new IOException("Connection error");

                            if (OnDataRecievingControl != null)
                            {
                                OnDataRecievingControl(this, line);
                            }
                        }
                        catch (Exception e)
                        {
                            if (OnStatusChanged != null)
                            {
                                OnStatusChanged(this, ipAddress.ToString() + ":" + port + " Connection Closing (reason:" + e.Message + " )\n");
                            }

                            //Connection closed
                            connectionManager.Connected = false;
                            sr.Close();
                            ns.Close();
                            socket.Close();
                            socket = null;
                            if (connectionFinishing)
                            {
                                return;
                            }
                        }
                    }
                }

            } while (socket == null);
        }

        /// <summary>
        /// send a command
        /// </summary>
        /// <param name="command"></param>
        public void sendCommand(String command)
        {
            Byte[] bytesToSend = Encoding.UTF8.GetBytes(command);

            if (socket != null)
            {
                try
                {
                    socket.Send(bytesToSend, bytesToSend.Length, SocketFlags.None);
                }
                catch (SocketException)
                {
                    socket.Close();
                }
            }
        }
    }
}
